#ifndef __EPOLL_SERVER_HPP__
#define __EPOLL_SERVER_HPP__

#include <functional>
#include <cassert>
#include "Epoll.hpp"
#include "Log.hpp"
#include "Sock.hpp"

using func_t = std::function<void(std::string)>;

class EpollServer
{
private:
    const static uint16_t defaultPort = 8080; // 将服务器的端口号默认为8080
    const static int defaultMaxEvents = 64; // 默认一次接收就绪事件的上限为64

public:
    EpollServer(func_t callBack, const uint16_t port = defaultPort, const int maxEvents = defaultMaxEvents)
        : _port(port)
        , _maxEvents(maxEvents)
        , _callBack(callBack)
    {
        // 1. 创建监听套接字
        _listenSock = Sock::Socket();
        Sock::Bind(_listenSock, _port);
        Sock::Listen(_listenSock);
        // 2. 创建epoll模型
        _epfd = Epoll::CreateEpoll();
        // 3. 将_listenSock添加到epoll模型中
        logMessage(DEBUG, "Init Server Success! _listenSock:%d _epfd:%d", _listenSock, _epfd); // 3 4
        if(!Epoll::CtlEpoll(_epfd, EPOLL_CTL_ADD, _listenSock, EPOLLIN))
            exit(6); // 只关心读事件
        // 4. 申请保存就绪事件的空间
        _readyEvents = new struct epoll_event[_maxEvents];
        logMessage(DEBUG, "Add _listenSock To Epoll Success");
    }

    ~EpollServer()
    {
        if(_listenSock >= 0) close(_listenSock);
        if(_epfd >= 0) close(_epfd);
        if(_readyEvents) delete[] _readyEvents;
    }

    void Start()
    {
        // int timeout = -1;   // 阻塞等待
        // int timeout = 0;    // 非阻塞等待
        int timeout = 3000; // 没事件就绪时,每隔3秒timeout一次
        while(true)
        {
            LoopOnce(timeout); // 循环一次
        }
    }

private:
    void LoopOnce(int timeout)
    {
        int n = Epoll::WaitEpoll(_epfd, _readyEvents, _maxEvents, timeout);
        // if(n == _maxEvents) // 如果获取上来的事件个数等于上限,可以选择扩容
        switch(n)
        {
            case -1:
                logMessage(WARNING, "WaitEpoll Error: %s", strerror(errno));
                break;
            case 0:
                logMessage(DEBUG, "WaitEpoll Timeout");
                break;
            default:
                // 事件就绪,需要处理事件
                logMessage(NORMAL, "WaitEpoll Success");
                HandlerEvents(n);
                break;
        }
    }

    void HandlerEvents(int n)
    {
        assert(n > 0); // 增强健壮性
        for(int i = 0; i < n; ++i)
        {
            uint32_t revents = _readyEvents[i].events;
            int sock = _readyEvents[i].data.fd;
            if(revents & EPOLLIN)
            {
                if(sock == _listenSock)
                {
                    Accepter(_listenSock);
                }
                else
                {
                    Recver(sock);
                }
            }
            if(revents & EPOLLOUT)
            {
                // TODO 写事件就绪将会在Reactor模式中处理
            }
        }
    }

    void Accepter(int listenSock)
    {   
        std::string clientIp;
        uint16_t clientPort;
        int sock = Sock::Accept(listenSock, &clientIp, &clientPort);
        if(sock < 0)
        {
            logMessage(WARNING, "Accept Error!");
            return;
        }
        // 获取连接成功不能立即调用read等接口,因为底层数据可能没有就绪而导致进程被挂起
        if(!Epoll::CtlEpoll(_epfd, EPOLL_CTL_ADD, sock, EPOLLIN)) return;
        logMessage(NORMAL, "Add New Sock %d To Epoll Success", sock);
    }

    void Recver(int sock)
    {
        char buffer[1024];
        int n = read(sock, buffer, sizeof(buffer) - 1);
        if(n > 0)
        {
            // 假设这里获取到的数据就是一个完整的报文
            // 但实际上不一定是一个完整的报文,需要通过
            // 定制协议来保证,在Reactor模式中统一讲解
            buffer[n] = 0;
            _callBack(buffer); // 将获取到的数据交给实际的业务进行处理,实现一定程度的解耦
        }
        else if(n == 0)
        {
            // 先让epoll不要关心该文件描述符了,然后才能关闭该文件描述符
            // 如果先关闭文件描述符的话,epoll将认为该文件描述符是无效的
            // 去除对文件描述符的关心,不需要设置时间
            bool ret = Epoll::CtlEpoll(_epfd, EPOLL_CTL_DEL, sock, 0); 
            assert(ret);
            (void)ret;
            close(sock);
            logMessage(NORMAL, "Client Quit, Me Too");
        }
        else
        {
            bool ret = Epoll::CtlEpoll(_epfd, EPOLL_CTL_DEL, sock, 0); 
            assert(ret);
            (void)ret;
            close(sock);
            logMessage(WARNING, "Read Error, Close Sock: %d", sock);
        }
    }

private:
    uint16_t _port;
    int _listenSock;
    int _epfd;
    struct epoll_event* _readyEvents; // 用于接收就绪事件的集合
    int _maxEvents; // 一次接收就绪事件的上限
    func_t _callBack; // 处理业务逻辑的回调函数
};

#endif